Passed
Push — master ( 5105f1...3c69a0 )
by EMP
01:17
created

main.js ➔ getClockIcon   A

Complexity

Conditions 5

Size

Total Lines 17
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 13
dl 0
loc 17
rs 9.2833
c 0
b 0
f 0
cc 5
1
"use strict";
2
3
sodium.ready.then(function() {
1 ignored issue
show
Bug introduced by
The variable sodium seems to be never declared. If this is a global, consider adding a /** global: sodium */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
4
5
const ae = new AllEars(function(ok) {
1 ignored issue
show
Bug introduced by
The variable AllEars seems to be never declared. If this is a global, consider adding a /** global: AllEars */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
6
	if (ok) {
7
		const greeting = localStorage.greeting;
1 ignored issue
show
Bug introduced by
The variable localStorage seems to be never declared. If this is a global, consider adding a /** global: localStorage */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
8
		if (greeting) {
9
			document.getElementById("greeting").textContent = greeting;
10
			document.getElementById("txt_pg").value = greeting;
11
		}
12
13
		document.getElementById("txt_skey").style.background = "#466";
14
		document.getElementById("txt_skey").maxLength = "64";
15
	} else {
16
		console.log("Failed to load All-Ears");
17
	}
18
});
19
20
function TabState(cur, max, btnDele, btnUpdt) {
21
	this.cur = cur;
22
	this.max = max;
23
	this.btnDele = btnDele;
24
	this.btnUpdt = btnUpdt;
25
}
26
27
const tabs = [
28
	new TabState(0, 0, false, true), // Inbox
29
	new TabState(0, 0, false, true), // Outbx
30
	new TabState(0, 1, true, false), // Write
31
	new TabState(0, 2, false, false), // Notes
32
	new TabState(0, 3, false, true) // Tools
33
];
34
35
let showHeaders = false;
36
37
let tab = 0;
38
const TAB_INBOX = 0;
39
const TAB_DRBOX = 1;
40
const TAB_WRITE = 2;
41
const TAB_NOTES = 3;
42
const TAB_TOOLS = 4;
43
44
// Helper functions
45
function getCountryName(countryCode) {
46
	switch (countryCode) {
47
		case "DZ": return "Algeria";
48
		case "AO": return "Angola";
49
		case "BJ": return "Benin";
50
		case "BW": return "Botswana";
51
		case "BF": return "Burkina Faso";
52
		case "BI": return "Burundi";
53
		case "CV": return "Cabo Verde";
54
		case "CM": return "Cameroon";
55
		case "CF": return "Central African Republic";
56
		case "TD": return "Chad";
57
		case "KM": return "Comoros";
58
		case "CD": return "Congo";
59
		case "DJ": return "Djibouti";
60
		case "EG": return "Egypt";
61
		case "GQ": return "Equatorial Guinea";
62
		case "ER": return "Eritrea";
63
		case "SZ": return "Eswatini";
64
		case "ET": return "Ethiopia";
65
		case "GA": return "Gabon";
66
		case "GM": return "Gambia";
67
		case "GH": return "Ghana";
68
		case "GW": return "Guinea-Bissau";
69
		case "GN": return "Guinea";
70
		case "CI": return "Ivory Coast";
71
		case "KE": return "Kenya";
72
		case "LS": return "Lesotho";
73
		case "LR": return "Liberia";
74
		case "LY": return "Libya";
75
		case "MG": return "Madagascar";
76
		case "MW": return "Malawi";
77
		case "ML": return "Mali";
78
		case "MR": return "Mauritania";
79
		case "MU": return "Mauritius";
80
		case "YT": return "Mayotte";
81
		case "MA": return "Morocco";
82
		case "MZ": return "Mozambique";
83
		case "NA": return "Namibia";
84
		case "NE": return "Niger";
85
		case "NG": return "Nigeria";
86
		case "CG": return "Republic of the Congo";
87
		case "RW": return "Rwanda";
88
		case "RE": return "Réunion";
89
		case "SH": return "Saint Helena";
90
		case "SN": return "Senegal";
91
		case "SC": return "Seychelles";
92
		case "SL": return "Sierra Leone";
93
		case "SO": return "Somalia";
94
		case "ZA": return "South Africa";
95
		case "SS": return "South Sudan";
96
		case "SD": return "Sudan";
97
		case "ST": return "São Tomé and Príncipe";
98
		case "TZ": return "Tanzania";
99
		case "TG": return "Togo";
100
		case "TN": return "Tunisia";
101
		case "UG": return "Uganda";
102
		case "EH": return "Western Sahara";
103
		case "ZM": return "Zambia";
104
		case "ZW": return "Zimbabwe";
105
		case "AQ": return "Antarctica";
106
		case "BV": return "Bouvet Island";
107
		case "TF": return "French Southern Territories";
108
		case "HM": return "Heard Island and McDonald Islands";
109
		case "GS": return "South Georgia and the South Sandwich Islands";
110
		case "AF": return "Afghanistan";
111
		case "AM": return "Armenia";
112
		case "AZ": return "Azerbaijan";
113
		case "BH": return "Bahrain";
114
		case "BD": return "Bangladesh";
115
		case "BT": return "Bhutan";
116
		case "IO": return "British Indian Ocean Territory";
117
		case "BN": return "Brunei";
118
		case "KH": return "Cambodia";
119
		case "CN": return "China";
120
		case "CC": return "Cocos [Keeling] Islands";
121
		case "GE": return "Georgia";
122
		case "JO": return "Hashemite Kingdom of Jordan";
123
		case "HK": return "Hong Kong";
124
		case "IN": return "India";
125
		case "ID": return "Indonesia";
126
		case "IR": return "Iran";
127
		case "IQ": return "Iraq";
128
		case "IL": return "Israel";
129
		case "JP": return "Japan";
130
		case "KZ": return "Kazakhstan";
131
		case "KW": return "Kuwait";
132
		case "KG": return "Kyrgyzstan";
133
		case "LA": return "Laos";
134
		case "LB": return "Lebanon";
135
		case "MO": return "Macao";
136
		case "MY": return "Malaysia";
137
		case "MV": return "Maldives";
138
		case "MN": return "Mongolia";
139
		case "MM": return "Myanmar";
140
		case "NP": return "Nepal";
141
		case "KP": return "North Korea";
142
		case "OM": return "Oman";
143
		case "PK": return "Pakistan";
144
		case "PS": return "Palestine";
145
		case "PH": return "Philippines";
146
		case "QA": return "Qatar";
147
		case "SA": return "Saudi Arabia";
148
		case "SG": return "Singapore";
149
		case "KR": return "South Korea";
150
		case "LK": return "Sri Lanka";
151
		case "SY": return "Syria";
152
		case "TW": return "Taiwan";
153
		case "TJ": return "Tajikistan";
154
		case "TH": return "Thailand";
155
		case "TR": return "Turkey";
156
		case "TM": return "Turkmenistan";
157
		case "AE": return "United Arab Emirates";
158
		case "UZ": return "Uzbekistan";
159
		case "VN": return "Vietnam";
160
		case "YE": return "Yemen";
161
		case "AL": return "Albania";
162
		case "AD": return "Andorra";
163
		case "AT": return "Austria";
164
		case "BY": return "Belarus";
165
		case "BE": return "Belgium";
166
		case "BA": return "Bosnia and Herzegovina";
167
		case "BG": return "Bulgaria";
168
		case "HR": return "Croatia";
169
		case "CY": return "Cyprus";
170
		case "CZ": return "Czechia";
171
		case "DK": return "Denmark";
172
		case "EE": return "Estonia";
173
		case "FO": return "Faroe Islands";
174
		case "FI": return "Finland";
175
		case "FR": return "France";
176
		case "DE": return "Germany";
177
		case "GI": return "Gibraltar";
178
		case "GR": return "Greece";
179
		case "GG": return "Guernsey";
180
		case "HU": return "Hungary";
181
		case "IS": return "Iceland";
182
		case "IE": return "Ireland";
183
		case "IM": return "Isle of Man";
184
		case "IT": return "Italy";
185
		case "JE": return "Jersey";
186
		case "XK": return "Kosovo";
187
		case "LV": return "Latvia";
188
		case "LI": return "Liechtenstein";
189
		case "LU": return "Luxembourg";
190
		case "MT": return "Malta";
191
		case "MC": return "Monaco";
192
		case "ME": return "Montenegro";
193
		case "NL": return "Netherlands";
194
		case "MK": return "North Macedonia";
195
		case "NO": return "Norway";
196
		case "PL": return "Poland";
197
		case "PT": return "Portugal";
198
		case "LT": return "Republic of Lithuania";
199
		case "MD": return "Republic of Moldova";
200
		case "RO": return "Romania";
201
		case "RU": return "Russia";
202
		case "SM": return "San Marino";
203
		case "RS": return "Serbia";
204
		case "SK": return "Slovakia";
205
		case "SI": return "Slovenia";
206
		case "ES": return "Spain";
207
		case "SJ": return "Svalbard and Jan Mayen";
208
		case "SE": return "Sweden";
209
		case "CH": return "Switzerland";
210
		case "UA": return "Ukraine";
211
		case "GB": return "United Kingdom";
212
		case "VA": return "Vatican City";
213
		case "AX": return "Åland";
214
		case "AI": return "Anguilla";
215
		case "AG": return "Antigua and Barbuda";
216
		case "AW": return "Aruba";
217
		case "BS": return "Bahamas";
218
		case "BB": return "Barbados";
219
		case "BZ": return "Belize";
220
		case "BM": return "Bermuda";
221
		case "BQ": return "Bonaire, Sint Eustatius, and Saba";
222
		case "VG": return "British Virgin Islands";
223
		case "CA": return "Canada";
224
		case "KY": return "Cayman Islands";
225
		case "CR": return "Costa Rica";
226
		case "CU": return "Cuba";
227
		case "CW": return "Curaçao";
228
		case "DM": return "Dominica";
229
		case "DO": return "Dominican Republic";
230
		case "SV": return "El Salvador";
231
		case "GL": return "Greenland";
232
		case "GD": return "Grenada";
233
		case "GP": return "Guadeloupe";
234
		case "GT": return "Guatemala";
235
		case "HT": return "Haiti";
236
		case "HN": return "Honduras";
237
		case "JM": return "Jamaica";
238
		case "MQ": return "Martinique";
239
		case "MX": return "Mexico";
240
		case "MS": return "Montserrat";
241
		case "NI": return "Nicaragua";
242
		case "PA": return "Panama";
243
		case "PR": return "Puerto Rico";
244
		case "BL": return "Saint Barthélemy";
245
		case "LC": return "Saint Lucia";
246
		case "MF": return "Saint Martin";
247
		case "PM": return "Saint Pierre and Miquelon";
248
		case "VC": return "Saint Vincent and the Grenadines";
249
		case "SX": return "Sint Maarten";
250
		case "KN": return "St Kitts and Nevis";
251
		case "TT": return "Trinidad and Tobago";
252
		case "TC": return "Turks and Caicos Islands";
253
		case "VI": return "U.S. Virgin Islands";
254
		case "US": return "United States";
255
		case "AS": return "American Samoa";
256
		case "AU": return "Australia";
257
		case "CX": return "Christmas Island";
258
		case "CK": return "Cook Islands";
259
		case "TL": return "Democratic Republic of Timor-Leste";
260
		case "FM": return "Federated States of Micronesia";
261
		case "FJ": return "Fiji";
262
		case "PF": return "French Polynesia";
263
		case "GU": return "Guam";
264
		case "KI": return "Kiribati";
265
		case "MH": return "Marshall Islands";
266
		case "NR": return "Nauru";
267
		case "NC": return "New Caledonia";
268
		case "NZ": return "New Zealand";
269
		case "NU": return "Niue";
270
		case "NF": return "Norfolk Island";
271
		case "MP": return "Northern Mariana Islands";
272
		case "PW": return "Palau";
273
		case "PG": return "Papua New Guinea";
274
		case "PN": return "Pitcairn Islands";
275
		case "WS": return "Samoa";
276
		case "SB": return "Solomon Islands";
277
		case "TK": return "Tokelau";
278
		case "TO": return "Tonga";
279
		case "TV": return "Tuvalu";
280
		case "UM": return "U.S. Minor Outlying Islands";
281
		case "VU": return "Vanuatu";
282
		case "WF": return "Wallis and Futuna";
283
		case "AR": return "Argentina";
284
		case "BO": return "Bolivia";
285
		case "BR": return "Brazil";
286
		case "CL": return "Chile";
287
		case "CO": return "Colombia";
288
		case "EC": return "Ecuador";
289
		case "FK": return "Falkland Islands";
290
		case "GF": return "French Guiana";
291
		case "GY": return "Guyana";
292
		case "PY": return "Paraguay";
293
		case "PE": return "Peru";
294
		case "SR": return "Suriname";
295
		case "UY": return "Uruguay";
296
		case "VE": return "Venezuela";
297
	}
0 ignored issues
show
Comprehensibility introduced by
There is no default case in this switch, so nothing gets returned when all cases fail. You might want to consider adding a default or return undefined explicitly.
Loading history...
298
}
299
300
function getCountryFlag(countryCode) {
301
	return sodium.to_string(new Uint8Array([
1 ignored issue
show
Bug introduced by
The variable sodium seems to be never declared. If this is a global, consider adding a /** global: sodium */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
302
		240, 159, 135, 166 + countryCode.codePointAt(0) - 65,
303
		240, 159, 135, 166 + countryCode.codePointAt(1) - 65
304
	]));
305
}
306
307
function getClockIcon(d) {
308
	const h24 = d.getUTCHours();
309
	let h12 = (h24 === 0 ? 12 : ((h24 > 12) ? h24 - 12 : h24));
310
311
	const m60 = (d.getUTCMinutes() * 60) + d.getUTCSeconds();
312
	let m30 = 0;
313
	if (m60 <= 900) { // <= 15: round down to this hour
314
		m30 = 0;
315
	} else if (m60 > 900 && m60 < 2700) { // 15..45: round to half-past this hour
316
		m30 = 12;
317
	} else { // >= 45: round up to next hour
318
		h12++;
319
		m30 = 0;
320
	}
321
322
	return "&#" + ((128335 + h12) + m30) + ";";
323
}
324
325
function getMsgId(num) {
326
	let i;
327
	if (ae.GetExtMsgHeaders(num).toLowerCase().slice(0, 11) === "message-id:") {
328
		i = 0;
329
	} else {
330
		i = ae.GetExtMsgHeaders(num).toLowerCase().indexOf("\nmessage-id:");
331
		if (i < 1) return "ERR";
332
		i++;
333
	}
334
335
	const x = ae.GetExtMsgHeaders(num).slice(i + 11).trim();
336
	if (x[0] !== "<") return "ERR2";
337
	return x.slice(1, x.indexOf(">"));
338
}
339
340
function clearDisplay() {
341
	let el = document.getElementById("midright").getElementsByTagName("img");
342
	if (el.length !== 1) el = document.getElementById("midright").getElementsByTagName("audio");
343
	if (el.length !== 1) el = document.getElementById("midright").getElementsByTagName("video");
344
	if (el.length !== 1) el = document.getElementById("midright").getElementsByTagName("embed");
345
	if (el.length !== 1) el = document.getElementById("midright").getElementsByTagName("iframe");
346
	if (el.length !== 1) return;
347
348
	URL.revokeObjectURL(el[0].src);
1 ignored issue
show
Bug introduced by
The variable URL seems to be never declared. If this is a global, consider adding a /** global: URL */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
349
	el[0].remove();
350
}
351
352
function displayFile(num) {
353
	const fileType = ae.GetUplMsgType(num);
354
355
	if (!fileType) { // Download
356
		const a = document.createElement("a");
357
		a.href = URL.createObjectURL(new Blob([ae.GetUplMsgBody(num).buffer]));
2 ignored issues
show
Bug introduced by
The variable Blob seems to be never declared. If this is a global, consider adding a /** global: Blob */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
Bug introduced by
The variable URL seems to be never declared. If this is a global, consider adding a /** global: URL */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
358
		a.download = ae.GetUplMsgTitle(num);
359
		a.click();
360
361
		URL.revokeObjectURL(a.href);
362
		a.href = "";
363
		a.download = "";
364
		return;
365
	}
366
367
	clearDisplay();
368
369
	document.getElementById("midright").scroll(0, 0);
370
	document.getElementById("midright").setAttribute("data-msgid", ae.GetUplMsgIdHex(num));
371
	document.getElementById("btn_reply").disabled = true;
372
	document.getElementById("btn_mdele").disabled = false;
373
	document.getElementById("midright").children[0].hidden = true;
374
	document.getElementById("midright").children[1].textContent = ae.GetUplMsgTitle(num);
375
376
	switch (fileType) {
377
		case "text": {
378
			document.getElementById("midright").children[2].hidden = false;
379
			document.getElementById("midright").children[2].textContent = sodium.to_string(ae.GetUplMsgBody(num));
1 ignored issue
show
Bug introduced by
The variable sodium seems to be never declared. If this is a global, consider adding a /** global: sodium */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
380
		break;}
381
382
		case "image": {
383
			document.getElementById("midright").children[2].hidden = true;
384
			const img = document.createElement("img");
385
			img.src = URL.createObjectURL(new Blob([ae.GetUplMsgBody(num).buffer]));
386
			document.getElementById("midright").appendChild(img);
387
388
			img.onclick = function() {
389
				if (!document.fullscreen)
390
					img.requestFullscreen();
391
				else
392
					document.exitFullscreen();
393
			};
394
		break;}
395
396
		case "audio": {
397
			document.getElementById("midright").children[2].hidden = true;
398
			const el = document.createElement("audio");
399
			el.controls = "controls";
400
			el.src = URL.createObjectURL(new Blob([ae.GetUplMsgBody(num).buffer]));
401
			document.getElementById("midright").appendChild(el);
402
		break;}
403
404
		case "video": {
405
			document.getElementById("midright").children[2].hidden = true;
406
			const el = document.createElement("video");
407
			el.controls = "controls";
408
			el.src = URL.createObjectURL(new Blob([ae.GetUplMsgBody(num).buffer]));
409
			document.getElementById("midright").appendChild(el);
410
		break;}
411
412
		case "pdf": {
413
			document.getElementById("midright").children[2].hidden = true;
414
			const el = document.createElement("embed");
415
			el.type = "application/pdf";
416
			el.src = URL.createObjectURL(new Blob([ae.GetUplMsgBody(num).buffer], {type: "application/pdf"}));
417
			document.getElementById("midright").appendChild(el);
418
		break;}
419
420
		case "html": {
421
			document.getElementById("midright").children[2].hidden = true;
422
			const el = document.createElement("iframe");
423
			el.allow = "";
424
			el.sandbox = "";
425
			el.csp = "base-uri 'none'; child-src 'none'; connect-src 'none'; default-src 'none'; font-src 'none'; form-action 'none'; frame-ancestors 'none'; frame-src 'none'; img-src 'none'; manifest-src 'none'; media-src 'none'; object-src 'none'; script-src 'none'; style-src 'none'; worker-src 'none';";
426
			el.srcdoc = sodium.to_string(ae.GetUplMsgBody(num).buffer);
427
			document.getElementById("midright").appendChild(el);
428
		break;}
429
	}
430
}
431
432
function displayMsg(isInt, num) {
433
	clearDisplay();
434
435
	document.getElementById("btn_mdele").disabled = false;
436
	document.getElementById("midright").scroll(0, 0);
437
	document.getElementById("midright").setAttribute("data-msgid", isInt? ae.GetIntMsgIdHex(num) : ae.GetExtMsgIdHex(num));
438
439
	const ts = isInt? ae.GetIntMsgTime(num) : ae.GetExtMsgTime(num);
440
441
	document.getElementById("btn_reply").disabled = false;
442
	document.getElementById("btn_reply").onclick = function() {
443
		document.getElementById("write_recv").value = isInt? ae.GetIntMsgFrom(num) : ae.GetExtMsgReplyAddress(num);
444
		document.getElementById("write_subj").value = isInt? ae.GetIntMsgTitle(num) : ae.GetExtMsgTitle(num);
445
		if (!document.getElementById("write_subj").value.startsWith("Re:")) document.getElementById("write_subj").value = "Re: " + document.getElementById("write_subj").value;
446
		document.querySelector("#write2_pkey > input").value = isInt? ae.GetIntMsgFromPk(num) : "";
447
448
		document.getElementById("write_recv").readOnly = !isInt;
449
		document.getElementById("write_subj").readOnly = !isInt;
450
		document.getElementById("write_subj").setAttribute("data-replyid", isInt? "" : getMsgId(num));
451
452
		tabs[TAB_WRITE].cur = 0;
453
		document.getElementById("btn_write").disabled = false;
454
		document.getElementById("btn_write").click();
455
		document.getElementById("write_body").focus();
456
457
		for (const opt of document.getElementById("write_from").options) {
458
			if (opt.value === (isInt ? ae.GetIntMsgTo(num) : ae.GetExtMsgTo(num))) {
459
				opt.selected = true;
460
			}
461
		}
462
	};
463
464
	document.getElementById("midright").children[0].hidden = false;
465
	document.getElementById("midright").children[2].hidden = false;
466
467
	if (isInt) {
468
		document.getElementById("midright").children[1].textContent = ae.GetIntMsgTitle(num);
469
		document.getElementById("midright").children[2].textContent = ae.GetIntMsgBody(num);
470
	} else {
471
		document.getElementById("midright").children[2].innerHTML = "";
472
473
		const headers = document.createElement("p");
474
		headers.textContent = ae.GetExtMsgHeaders(num);
475
		headers.className = "mono";
476
		headers.hidden = !showHeaders;
477
		document.getElementById("midright").children[2].appendChild(headers);
478
479
		const body = document.createElement("p");
480
		body.textContent = ae.GetExtMsgBody(num);
481
		document.getElementById("midright").children[2].appendChild(body);
482
483
		document.getElementById("midright").children[1].textContent = ae.GetExtMsgTitle(num);
484
		document.getElementById("midright").children[1].onclick = function() {showHeaders = !showHeaders; headers.hidden = !showHeaders;};
485
		document.getElementById("midright").children[1].style.cursor = "pointer";
486
	}
487
488
	document.getElementById("readmsg_envto").textContent = isInt ? ae.GetIntMsgTo(num) : ae.GetExtMsgTo(num);
489
490
	const tzOs = new Date().getTimezoneOffset();
491
	const tz = ((tzOs < 0) ? "+" : "-") + Math.floor(tzOs / -60).toString().padStart(2, "0") + (tzOs % 60 * -1).toString().padStart(2, "0");
492
	const msgDate = new Date((ts * 1000) + (tzOs * -60000));
493
494
	document.getElementById("readmsg_date").children[0].innerHTML = getClockIcon(msgDate);
495
	document.getElementById("readmsg_date").children[1].textContent = msgDate.toISOString().slice(0, 19).replace("T", " ") + " " + tz;
496
497
	if (!isInt) {
498
		document.getElementById("readmsg_ip").hidden = false;
499
		document.getElementById("readmsg_country").hidden = false;
500
		document.getElementById("readmsg_tls").hidden = false;
501
		document.getElementById("readmsg_greet").hidden = false;
502
503
		const cc = ae.GetExtMsgCountry(num);
504
505
		document.getElementById("readmsg_ip").children[0].textContent = ae.GetExtMsgIp(num);
506
		document.getElementById("readmsg_country").textContent = getCountryFlag(cc) + " " + getCountryName(cc);
507
		document.getElementById("readmsg_tls").children[0].textContent = ae.GetExtMsgTLS(num);
508
		document.getElementById("readmsg_greet").children[0].textContent = ae.GetExtMsgGreet(num);
509
		document.getElementById("readmsg_envfrom").textContent = ae.GetExtMsgFrom(num);
510
511
		let flagText = "";
512
		if (!ae.GetExtMsgFlagVPad(num)) flagText += "<abbr title=\"Invalid padding\">PAD</abbr> ";
513
		if (!ae.GetExtMsgFlagVSig(num)) flagText += "<abbr title=\"Invalid signature\">SIG</abbr> ";
514
		if (!ae.GetExtMsgFlagPExt(num)) flagText += "<abbr title=\"The sender did not use the Extended (ESMTP) protocol\">SMTP</abbr> ";
515
		if (!ae.GetExtMsgFlagQuit(num)) flagText += "<abbr title=\"The sender did not issue the required QUIT command\">QUIT</abbr> ";
516
		if (ae.GetExtMsgFlagRare(num)) flagText += "<abbr title=\"The sender issued unusual command(s)\">RARE</abbr> ";
517
		if (ae.GetExtMsgFlagFail(num)) flagText += "<abbr title=\"The sender issued invalid command(s)\">FAIL</abbr> ";
518
		if (ae.GetExtMsgFlagPErr(num)) flagText += "<abbr title=\"The sender violated the protocol\">PROT</abbr> ";
519
		document.getElementById("readmsg_flags").children[0].innerHTML = flagText.trim();
520
	} else {
521
		document.getElementById("readmsg_ip").hidden = true;
522
		document.getElementById("readmsg_country").hidden = true;
523
		document.getElementById("readmsg_greet").hidden = true;
524
525
		document.getElementById("readmsg_tls").hidden = false;
526
		document.getElementById("readmsg_tls").children[0].textContent = ae.GetIntMsgFromPk(num);
527
528
		let symbol = "<span title=\"Invalid level\">&#x26a0;</span>";
529
		if (ae.GetIntMsgFrom(num) === "system") {if (ae.GetIntMsgLevel(num) === 3) symbol = "<span title=\"System\">&#x1f162;</span>";} // S (System)
530
		else if (ae.GetIntMsgLevel(num) === 0) symbol = "<span title=\"Level 0 User\">&#x1f10c;</span>"; // 0
531
		else if (ae.GetIntMsgLevel(num) === 1) symbol = "<span title=\"Level 1 User\">&#x278a;</span>"; // 1
532
		else if (ae.GetIntMsgLevel(num) === 2) symbol = "<span title=\"Level 2 User\">&#x278b;</span>"; // 2
533
		else if (ae.GetIntMsgLevel(num) === 3) symbol = "<span title=\"Administrator\">&#x1f150;</span>"; // A (Admin)
534
		document.getElementById("readmsg_envfrom").innerHTML = symbol + " " + ae.GetIntMsgFrom(num);
535
536
		let flagText = "";
537
		if (!ae.GetIntMsgFlagVPad(num)) flagText += "<abbr title=\"Invalid padding\">PAD</abbr> ";
538
		if (!ae.GetIntMsgFlagVSig(num)) flagText += "<abbr title=\"Invalid signature\">SIG</abbr> ";
539
		if (ae.GetIntMsgFlagE2ee(num)) flagText += "<abbr title=\"End-to-end encrypted\">E2EE</abbr> ";
540
		document.getElementById("readmsg_flags").children[0].innerHTML = flagText.trim();
541
	}
542
}
543
544
// Interface
545
function addMsg(isInt, i) {
546
	const row = document.getElementById("tbl_inbox").insertRow(-1);
547
	row.setAttribute("data-msgid", isInt? ae.GetIntMsgIdHex(i) : ae.GetExtMsgIdHex(i));
548
549
	const cellTime = row.insertCell(-1);
550
	const ts = isInt? ae.GetIntMsgTime(i) : ae.GetExtMsgTime(i);
551
	cellTime.setAttribute("data-ts", ts);
552
	cellTime.textContent = new Date((ts * 1000) + (new Date().getTimezoneOffset() * -60000)).toISOString().slice(0, 10);
553
554
	const cellSubj = row.insertCell(-1);
555
	cellSubj.textContent = isInt? ae.GetIntMsgTitle(i) : ae.GetExtMsgTitle(i);
556
557
	if (isInt) {
558
		const cellSnd = row.insertCell(-1);
559
		cellSnd.textContent = ae.GetIntMsgFrom(i);
560
		cellSnd.className = (ae.GetIntMsgFrom(i).length === 16) ? "mono" : "";
561
	} else {
562
		const from1 = ae.GetExtMsgFrom(i);
563
		const from2 = from1.substring(from1.indexOf("@") + 1);
564
		const cc = ae.GetExtMsgCountry(i);
565
		const cellSnd1 = row.insertCell(-1);
566
		cellSnd1.textContent = from1.substring(0, from1.indexOf("@"));
567
568
		const flag = document.createElement("abbr");
569
		flag.textContent = getCountryFlag(cc);
570
		flag.title = getCountryName(cc);
571
572
		const fromText = document.createElement("span");
573
		fromText.textContent = " " + from2;
574
575
		const cellSnd2 = row.insertCell(-1);
576
		cellSnd2.appendChild(flag);
577
		cellSnd2.appendChild(fromText);
578
	}
579
580
	row.onclick = function() {
581
		displayMsg(isInt, i);
582
	};
583
}
584
585
function getRowsPerPage() {
586
	const tbl = document.getElementById("tbl_inbox");
587
	tbl.innerHTML = "";
588
	const row = tbl.insertRow(-1);
589
	const cell = row.insertCell(-1);
590
	cell.textContent = "0";
591
592
	const rowsPerPage = Math.floor(getComputedStyle(document.getElementById("div_inbox")).height.replace("px", "") / getComputedStyle(document.querySelector("#tbl_inbox > tbody > tr:first-child")).height.replace("px", "")) - 1; // -1 allows space for 'load more'
593
	tbl.innerHTML = "";
594
	return rowsPerPage;
595
}
596
597
function addMessages() {
598
	const rowsPerPage = getRowsPerPage();
599
	let skipMsgs = rowsPerPage * tabs[TAB_INBOX].cur;
600
601
	const maxExt = ae.GetExtMsgCount();
602
	const maxInt = ae.GetIntMsgCount();
603
604
	tabs[TAB_INBOX].max = Math.floor((maxExt + maxInt - 1) / rowsPerPage);
605
606
	let numExt = 0;
607
	let numInt = 0;
608
	let numAdd = 0;
609
610
	while (numAdd < rowsPerPage) {
611
		const tsInt = (numInt < maxInt) ? ae.GetIntMsgTime(numInt) : -1;
612
		const tsExt = (numExt < maxExt) ? ae.GetExtMsgTime(numExt) : -1;
613
		if (tsInt === -1 && tsExt === -1) break;
614
615
		if (tsInt !== -1 && (tsExt === -1 || tsInt > tsExt)) {
616
			if (skipMsgs > 0) skipMsgs--; else {addMsg(true, numInt); numAdd++;}
617
			numInt++;
618
		} else if (tsExt !== -1) {
619
			if (skipMsgs > 0) skipMsgs--; else {addMsg(false, numExt); numAdd++;}
620
			numExt++;
621
		}
622
	}
623
624
	if (ae.GetReadyMsgBytes() < ae.GetTotalMsgBytes()) {
625
		const inbox = document.getElementById("tbl_inbox");
626
		const row = inbox.insertRow(-1);
627
		const cell = row.insertCell(-1);
628
		cell.textContent = "Load more (" + Math.round((ae.GetTotalMsgBytes() - ae.GetReadyMsgBytes()) / 1024) + " KiB left)";
629
630
		row.onclick = function() {
631
			this.onclick = "";
632
633
			ae.Message_Browse(false, false, function(successBrowse) {
634
				document.getElementById("tbl_inbox").style.opacity = 1;
635
636
				if (successBrowse) {
637
					addMessages();
638
					addUploads();
639
					addSent();
640
					if (tabs[tab].cur < tabs[tab].max) document.getElementById("btn_rght").disabled = false;
641
				}
642
			});
643
		};
644
	}
645
}
646
647
function addUploads() {
648
	const tbl = document.getElementById("tbd_uploads");
649
	tbl.innerHTML = "";
650
651
	for (let i = 0; i < ae.GetUplMsgCount(); i++) {
652
		const row = tbl.insertRow(-1);
653
		row.setAttribute("data-msgid", ae.GetUplMsgIdHex(i));
654
655
		let cell;
656
		cell = row.insertCell(-1); cell.textContent = new Date(ae.GetUplMsgTime(i) * 1000).toISOString().slice(0, 10);
657
658
		cell = row.insertCell(-1); cell.textContent = ae.GetUplMsgTitle(i);
659
		cell.onclick = function() {displayFile(this.parentElement.rowIndex - 1);};
660
661
		cell = row.insertCell(-1); cell.textContent = (ae.GetUplMsgBytes(i) / 1024).toFixed(1);
662
663
		cell = row.insertCell(-1);
664
		if (ae.GetUplMsgIdHex(i)) {
665
			cell.innerHTML = "<button data-msgid=\"" + ae.GetUplMsgIdHex(i) + "\" type=\"button\">X</button>";
666
667
			cell.children[0].onclick = function() {
668
				const tr = this.parentElement.parentElement;
669
				ae.Message_Delete(this.getAttribute("data-msgid"), function(success) {
670
					if (success) tr.remove();
671
				});
672
			};
673
		}
674
	}
675
}
676
677
function displayOutMsg(num) {
678
	clearDisplay();
679
	document.getElementById("midright").scroll(0, 0);
680
	document.getElementById("midright").setAttribute("data-msgid", ae.GetOutMsgIdHex(num));
681
	document.getElementById("btn_reply").disabled = true;
682
	document.getElementById("btn_mdele").disabled = false;
683
	document.getElementById("midright").children[0].hidden = false;
684
	document.getElementById("midright").children[2].hidden = false;
685
686
	document.getElementById("midright").children[1].textContent = ae.GetOutMsgSubj(num);
687
	document.getElementById("midright").children[2].textContent = ae.GetOutMsgBody(num);
688
689
	document.getElementById("readmsg_envto").textContent = ae.GetOutMsgTo(num);
690
	document.getElementById("readmsg_envfrom").textContent = ae.GetOutMsgFrom(num);
691
	document.getElementById("readmsg_to").textContent = ae.GetOutMsgMxDom(num);
692
693
	const ts = ae.GetOutMsgTime(num);
694
	const tzOs = new Date().getTimezoneOffset();
695
	const tz = ((tzOs < 0) ? "+" : "-") + Math.floor(tzOs / -60).toString().padStart(2, "0") + (tzOs % 60 * -1).toString().padStart(2, "0");
696
	document.getElementById("readmsg_date").children[1].textContent = new Date((ts * 1000) + (tzOs * -60000)).toISOString().slice(0, 19).replace("T", " ") + " " + tz;
697
698
	const isInt = ae.GetOutMsgIsInt(num);
699
	document.getElementById("readmsg_ip").hidden      = isInt;
700
	document.getElementById("readmsg_country").hidden = isInt;
701
	document.getElementById("readmsg_tls").hidden     = isInt;
702
	document.getElementById("readmsg_greet").hidden   = isInt;
703
704
	if (!isInt) {
705
//		const cc = ae.GetExtMsgCountry(num);
706
707
		document.getElementById("readmsg_ip").children[0].textContent = ae.GetOutMsgIp(num);
708
//		document.getElementById("readmsg_country").textContent = getCountryFlag(cc) + " " + getCountryName(cc);
709
//		document.getElementById("readmsg_tls").children[0].textContent = ae.GetOutMsgTLS(num);
710
		document.getElementById("readmsg_greet").children[0].textContent = ae.GetOutMsgGreet(num);
711
	}
712
713
	let flagText = "";
714
	if (!ae.GetOutMsgFlagVPad(num)) flagText += "<abbr title=\"Invalid padding\">PAD</abbr> ";
715
	if (!ae.GetOutMsgFlagVSig(num)) flagText += "<abbr title=\"Invalid signature\">SIG</abbr> ";
716
	if (ae.GetOutMsgFlagE2ee(num)) flagText += "<abbr title=\"End-to-end encrypted\">E2EE</abbr> ";
717
	document.getElementById("readmsg_flags").children[0].innerHTML = flagText.trim();
718
}
719
720
function addSent() {
721
	const tbl = document.getElementById("tbl_drbox");
722
	tbl.innerHTML = "";
723
724
	for (let i = 0; i < ae.GetOutMsgCount(); i++) {
725
		const row = tbl.insertRow(-1);
726
		row.setAttribute("data-msgid", ae.GetOutMsgIdHex(i));
727
728
		let cell;
729
		cell = row.insertCell(-1); cell.textContent = new Date(ae.GetOutMsgTime(i) * 1000).toISOString().slice(0, 10);
730
		cell = row.insertCell(-1); cell.textContent = ae.GetOutMsgSubj(i);
731
		row.onclick = function() {displayOutMsg(i);};
732
	}
733
}
734
735
function updateAddressCounts() {
736
	document.getElementById("limit_normal").textContent = (ae.GetAddressCountNormal() + "/" + ae.GetLimitNormalA(ae.GetUserLevel())).padStart(ae.GetLimitNormalA(ae.GetUserLevel()) > 9 ? 5 : 1);
737
	document.getElementById("limit_shield").textContent = (ae.GetAddressCountShield() + "/" + ae.GetLimitShieldA(ae.GetUserLevel())).padStart(ae.GetLimitShieldA(ae.GetUserLevel()) > 9 ? 5 : 1);
738
	document.getElementById("limit_total").textContent = ((ae.GetAddressCountNormal() + ae.GetAddressCountShield()) + "/" + ae.GetAddrPerUser()).padStart(5);
739
740
	const limitReached = (ae.GetAddressCountNormal() + ae.GetAddressCountShield() >= 31);
741
	document.getElementById("btn_address_create_normal").disabled = (limitReached || ae.GetAddressCountNormal() >= ae.GetLimitNormalA(ae.GetUserLevel()));
742
	document.getElementById("btn_address_create_shield").disabled = (limitReached || ae.GetAddressCountShield() >= ae.GetLimitShieldA(ae.GetUserLevel()));
743
}
744
745
function adjustLevel(pubkey, level, c) {
746
	const fs = document.getElementById("fs_accs");
747
	fs.disabled = true;
748
749
	ae.Account_Update(pubkey, level, function(success) {
750
		fs.disabled = false;
751
752
		if (success) {
753
			c[4].textContent = level;
754
			c[5].children[0].disabled = (level === 3);
755
			c[6].children[0].disabled = (level === 0);
756
		}
757
	});
758
}
759
760
function addAccountToTable(i) {
761
	const tblAccs = document.getElementById("tbd_accs");
762
	const row = tblAccs.insertRow(-1);
763
	let cell;
764
	cell = row.insertCell(-1); cell.textContent = ae.Admin_GetUserPkHex(i);
765
	cell = row.insertCell(-1); cell.textContent = ae.Admin_GetUserSpace(i);
766
	cell = row.insertCell(-1); cell.textContent = ae.Admin_GetUserNAddr(i);
767
	cell = row.insertCell(-1); cell.textContent = ae.Admin_GetUserSAddr(i);
768
	cell = row.insertCell(-1); cell.textContent = ae.Admin_GetUserLevel(i);
769
770
	cell = row.insertCell(-1); cell.innerHTML = "<button type=\"button\" autocomplete=\"off\">+</button>";
771
	cell.children[0].onclick = function() {const c = this.parentElement.parentElement.cells; adjustLevel(c[0].textContent, parseInt(c[4].textContent, 10) + 1, c);};
772
	cell.children[0].disabled = (ae.Admin_GetUserLevel(i) === 3);
773
774
	cell = row.insertCell(-1); cell.innerHTML = "<button type=\"button\" autocomplete=\"off\">&minus;</button>";
775
	cell.children[0].onclick = function() {const c = this.parentElement.parentElement.cells; adjustLevel(c[0].textContent, parseInt(c[4].textContent, 10) - 1, c);};
776
	cell.children[0].disabled = (ae.Admin_GetUserLevel(i) === 0);
777
778
	cell = row.insertCell(-1); cell.innerHTML = "<button type=\"button\" autocomplete=\"off\">X</button>";
779
	cell.children[0].onclick = function() {
780
		const tr = this.parentElement.parentElement;
781
		ae.Account_Delete(tr.cells[0].textContent, function(success) {
782
			if (success) tr.remove();
783
		});
784
	};
785
}
786
787
function reloadAccount() {
788
	// Limits
789
	const tblLimits = document.getElementById("tbl_limits");
790
	if (ae.IsUserAdmin()) {
791
		for (let i = 0; i < 4; i++) {
792
			tblLimits.rows[i].cells[1].children[0].disabled = false;
793
			tblLimits.rows[i].cells[2].children[0].disabled = false;
794
			tblLimits.rows[i].cells[3].children[0].disabled = false;
795
796
			tblLimits.rows[i].cells[1].children[0].value = ae.GetLimitStorage(i) + 1;
797
			tblLimits.rows[i].cells[2].children[0].value = ae.GetLimitNormalA(i);
798
			tblLimits.rows[i].cells[3].children[0].value = ae.GetLimitShieldA(i);
799
		}
800
	} else {
801
		const lvl = ae.GetUserLevel();
802
		tblLimits.rows[lvl].cells[1].children[0].value = ae.GetLimitStorage(lvl) + 1;
803
		tblLimits.rows[lvl].cells[2].children[0].value = ae.GetLimitNormalA(lvl);
804
		tblLimits.rows[lvl].cells[3].children[0].value = ae.GetLimitShieldA(lvl);
805
	}
806
807
	// Accounts
808
	const tblAccs = document.getElementById("tbd_accs");
809
810
	// All: Our account
811
	const row = tblAccs.insertRow(-1);
812
	let cell;
813
	cell = row.insertCell(-1); cell.textContent = ae.GetUserPkHex();
814
	cell = row.insertCell(-1); cell.textContent = Math.round(ae.GetTotalMsgBytes() / 1024 / 1024);
815
	cell = row.insertCell(-1); cell.textContent = ae.GetAddressCountNormal();
816
	cell = row.insertCell(-1); cell.textContent = ae.GetAddressCountShield();
817
	cell = row.insertCell(-1); cell.textContent = ae.GetUserLevel();
818
	cell = row.insertCell(-1); cell.innerHTML = "<button type=\"button\" autocomplete=\"off\" disabled=\"disabled\">+</button>";
819
820
	cell = row.insertCell(-1); cell.innerHTML = "<button id=\"btn_downme\" type=\"button\" autocomplete=\"off\" disabled=\"disabled\">&minus;</button>";
821
	cell.children[0].onclick = function() {
822
		const newLevel = parseInt(row.cells[4].textContent, 10) - 1;
823
		ae.Account_Update(ae.GetUserPkHex(), newLevel, function(success) {
824
			if (success) row.cells[4].textContent = newLevel;
825
		});
826
	};
827
828
	cell = row.insertCell(-1); cell.innerHTML = "<button id=\"btn_killme\" type=\"button\" autocomplete=\"off\" disabled=\"disabled\">X</button>";
829
	cell.children[0].onclick = function() {
830
		ae.Account_Delete(ae.GetUserPkHex(), function(success) {
831
			if (success) row.remove();
832
		});
833
	};
834
835
	document.getElementById("txt_reg").disabled = !ae.IsUserAdmin();
836
	document.getElementById("btn_reg").disabled = !ae.IsUserAdmin();
837
838
	// Contacts
839
	for (let i = 0; i < ae.GetContactCount(); i++) {
840
		addContact(
841
			ae.GetContactMail(i),
842
			ae.GetContactName(i),
843
			ae.GetContactNote(i)
844
		);
845
	}
846
847
	// Addresses
848
	for (let i = 0; i < ae.GetAddressCount(); i++) {
849
		addAddress(i);
850
	}
851
852
	updateAddressCounts();
853
	addMessages();
854
	addUploads();
855
	addSent();
856
857
	document.getElementById("btn_rght").disabled = (tabs[tab].cur === tabs[tab].max);
858
}
859
860
function deleteAddress(addr) {
861
	let btns = document.getElementById("tbl_addrs").getElementsByTagName("button");
862
	for (let i = 0; i < btns.length; i++) btns[i].disabled = true;
863
864
	let addressToDelete = -1;
865
866
	for (let i = 0; i < ae.GetAddressCount(); i++) {
867
		if (addr === ae.GetAddress(i)) {
868
			addressToDelete = i;
869
			break;
870
		}
871
	}
872
873
	if (addressToDelete === -1) return;
874
875
	ae.Address_Delete(addressToDelete, function(success) {
876
		if (success) {
877
			document.getElementById("tbl_addrs").deleteRow(addressToDelete);
878
			document.getElementById("write_from").remove(addressToDelete);
879
			updateAddressCounts();
880
881
			const limitReached = (ae.GetAddressCountNormal() + ae.GetAddressCountShield() >= 31);
882
			document.getElementById("btn_address_create_normal").disabled = (limitReached || ae.GetAddressCountNormal() > ae.GetLimitNormalA(ae.GetUserLevel()));
883
			document.getElementById("btn_address_create_shield").disabled = (limitReached || ae.GetAddressCountShield() > ae.GetLimitShieldA(ae.GetUserLevel()));
884
885
			ae.Private_Update(function(success2) {
886
				if (!success2) console.log("Failed to update the Private field");
887
888
				btns = document.getElementById("tbl_addrs").getElementsByTagName("button");
889
				for (let i = 0; i < btns.length; i++) btns[i].disabled = false;
890
			});
891
		} else {
892
			console.log("Failed to delete address");
893
894
			btns = document.getElementById("tbl_addrs").getElementsByTagName("button");
895
			for (let i = 0; i < btns.length; i++) btns[i].disabled = false;
896
		}
897
	});
898
}
899
900 View Code Duplication
function shieldMix(addr) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
901
	let newAddr = "";
902
903
	for (let i = 0; i < 16; i++) {
904
		switch (addr.charAt(i)) {
905
			case '1':
906
				newAddr += "1iIlL".charAt(Math.floor(Math.random() * 5));
907
				break;
908
			case '0':
909
				newAddr += "0oO".charAt(Math.floor(Math.random() * 3));
910
				break;
911
			case 'w':
912
				newAddr += "VvWw".charAt(Math.floor(Math.random() * 4));
913
				break;
914
			default:
915
				newAddr += (Math.random() > 0.5) ? addr.charAt(i) : addr.charAt(i).toUpperCase();
916
		}
917
	}
918
919
	return newAddr;
920
}
921
922
function addAddress(num) {
923
	const addrTable = document.getElementById("tbl_addrs");
924
	const row = addrTable.insertRow(-1);
925
	const cellAddr = row.insertCell(-1);
926
	const cellChk1 = row.insertCell(-1);
927
	const cellChk2 = row.insertCell(-1);
928
	const cellBtnD = row.insertCell(-1);
929
930
	cellAddr.textContent = ae.GetAddress(num);
931
	cellAddr.onclick = function() {
932
		if (cellAddr.textContent.length === 16)
933
			navigator.clipboard.writeText(shieldMix(cellAddr.textContent) + "@" + ae.GetDomainEml());
1 ignored issue
show
Bug introduced by
The variable navigator seems to be never declared. If this is a global, consider adding a /** global: navigator */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
934
		else
935
			navigator.clipboard.writeText(cellAddr.textContent + "@" + ae.GetDomainEml());
936
	};
937
938
	cellChk1.innerHTML = ae.GetAddressAccExt(num) ? "<input type=\"checkbox\" checked=\"checked\">" : "<input type=\"checkbox\">";
939
	cellChk2.innerHTML = ae.GetAddressAccInt(num) ? "<input type=\"checkbox\" checked=\"checked\">" : "<input type=\"checkbox\">";
940
941
	cellBtnD.innerHTML = "<button type=\"button\">X</button>";
942
	cellBtnD.onclick = function() {deleteAddress(cellAddr.textContent);};
943
944
	const opt = document.createElement("option");
945
	opt.value = cellAddr.textContent;
946
	opt.textContent = cellAddr.textContent + "@" + ae.GetDomainEml();
947
	document.getElementById("write_from").appendChild(opt);
948
}
949
950
document.getElementById("btn_dele").onclick = function() {
951
	this.blur();
952
953
	if (tab === TAB_WRITE) {
954
		tabs[tab].cur = 0;
955
		updateTab();
956
957
		document.getElementById("write_recv").value = "";
958
		document.getElementById("write_subj").value = "";
959
		document.getElementById("write_body").value = "";
960
961
		document.getElementById("write_recv").readOnly = false;
962
		document.getElementById("write_subj").readOnly = false;
963
		document.getElementById("write_subj").setAttribute("data-replyid", "");
964
965
		document.getElementById("write_recv").focus();
966
	}
967
};
968
969
document.getElementById("btn_updt").onclick = function() {
970
	const btn = this;
971
	btn.disabled = true;
972
	btn.blur();
973
974
	if (tab === TAB_INBOX) {
975
		document.getElementById("tbl_inbox").style.opacity = 0.5;
976
977
		ae.Message_Browse(true, false, function(successBrowse) {
978
			document.getElementById("tbl_inbox").style.opacity = 1;
979
980
			if (successBrowse) {
981
				addMessages();
982
				addUploads();
983
				btn.disabled = false;
984
			} else {
985
				console.log("Failed to refresh");
986
				btn.disabled = false;
987
			}
988
		});
989
	}
990
};
991
992
document.getElementById("btn_mdele").onclick = function() {
993
	const btn = this;
994
	btn.blur();
995
	btn.disabled = true;
996
997
	const delId = document.getElementById("midright").getAttribute("data-msgid");
998
	if (!delId) return;
999
1000
	ae.Message_Delete(delId, function(success) {
1001
		if (success) {
1002
			["tbl_inbox", "tbl_drbox", "tbd_uploads"].forEach(function(tbl_name) {
1003
				const tbl = document.getElementById(tbl_name);
1004
				for (let i = 0; i < tbl.rows.length; i++) {if (tbl.rows[i].getAttribute("data-msgid") === delId) tbl.deleteRow(i);}
1005
			});
1006
1007
			addMessages();
1008
			addUploads();
1009
			addSent();
1010
		} else btn.disabled = false;
1011
	});
1012
};
1013
1014
function addContact(mail, name, note) {
1015
	const tbl = document.getElementById("tbl_ctact");
1016
	const row = tbl.insertRow(-1);
1017
	const cellMail = row.insertCell(-1);
1018
	const cellName = row.insertCell(-1);
1019
	const cellNote = row.insertCell(-1);
1020
	const cellBtnD = row.insertCell(-1);
1021
1022
	cellMail.textContent = mail;
1023
	cellName.textContent = name;
1024
	cellNote.textContent = note;
1025
	cellBtnD.innerHTML = "<button type=\"button\">X</button>";
1026
1027
	cellMail.contentEditable = true;
1028
	cellName.contentEditable = true;
1029
	cellNote.contentEditable = true;
1030
1031
	cellBtnD.onclick = function() {row.remove();};
1032
}
1033
1034
document.getElementById("btn_newcontact").onclick = function() {
1035
	addContact("", "", "");
1036
};
1037
1038
document.getElementById("btn_savecontacts").onclick = function() {
1039
	while (ae.GetContactCount() > 0) {
1040
		ae.DeleteContact(0);
1041
	}
1042
1043
	for (const row of document.getElementById("tbl_ctact").rows) {
1044
		ae.AddContact(row.cells[0].textContent, row.cells[1].textContent, row.cells[2].textContent);
1045
	}
1046
1047
	const btn = this;
1048
	btn.disabled = true;
1049
1050
	ae.Private_Update(function(success) {
1051
		btn.disabled = false;
1052
1053
		if (!success) {
1054
			console.log("Failed contacts update");
1055
		}
1056
	});
1057
};
1058
1059
function writeVerify() {
1060
	if (
1061
	   !document.getElementById("write_recv").reportValidity()
1062
	|| !document.getElementById("write_subj").reportValidity()
1063
	|| !document.getElementById("write_body").reportValidity()
1064
	) {tabs[TAB_WRITE].cur = 0; return;}
1065
1066
	document.getElementById("div_write_1").hidden = true;
1067
	document.getElementById("div_write_2").hidden = false;
1068
1069
	document.getElementById("write2_recv").textContent = document.getElementById("write_recv").value;
1070
	document.getElementById("write2_subj").textContent = document.getElementById("write_subj").value;
1071
	document.getElementById("write2_rply").textContent = document.getElementById("write_subj").getAttribute("data-replyid");
1072
	document.getElementById("write2_body").textContent = document.getElementById("write_body").value;
1073
1074
	if (document.getElementById("write_recv").value.indexOf("@") >= 0) {
1075
		document.getElementById("write2_from").textContent = document.getElementById("write_from").value + "@" + ae.GetDomainEml();
1076
		document.getElementById("write2_pkey").hidden = true;
1077
	} else {
1078
		document.getElementById("write2_from").textContent = document.getElementById("write_from").value;
1079
		document.getElementById("write2_pkey").hidden = false;
1080
	}
1081
}
1082
1083
function updateTab() {
1084
	switch (tab) {
1085
		case TAB_INBOX:
1086
			addMessages();
1087
		break;
1088
1089
		case TAB_DRBOX:
1090
			addSent();
1091
		break;
1092
1093
		case TAB_WRITE:
1094
			switch (tabs[tab].cur) {
1095
				case 0:
1096
					document.getElementById("div_write_1").hidden = false;
1097
					document.getElementById("div_write_2").hidden = true;
1098
					document.getElementById("write_body").focus();
1099
					document.querySelector("#write2_send > button").disabled = false;
1100
					document.getElementById("write2_btntxt").textContent = "Send to";
1101
				break;
1102
1103
				case 1:
1104
					writeVerify();
1105
				break;
1106
			}
1107
		break;
1108
1109
		case TAB_NOTES:
1110
			for (let i = 0; i <= tabs[tab].max; i++) {
1111
				document.getElementById("div_notes").children[i].hidden = (i !== tabs[tab].cur);
1112
			}
1113
		break;
1114
1115
		case TAB_TOOLS:
1116
			for (let i = 0; i <= tabs[tab].max; i++) {
1117
				document.getElementById("div_tools").children[i].hidden = (i !== tabs[tab].cur);
1118
			}
1119
		break;
1120
	}
1121
1122
	document.getElementById("btn_left").disabled = (tabs[tab].cur === 0);
1123
	document.getElementById("btn_rght").disabled = (tabs[tab].cur === tabs[tab].max);
1124
}
1125
1126
document.getElementById("btn_left").onclick = function() {
1127
	tabs[tab].cur--;
1128
	if (tabs[tab].cur === 0) this.disabled = true;
1129
	if (tabs[tab].cur < tabs[tab].max) document.getElementById("btn_rght").disabled = false;
1130
	updateTab();
1131
	this.blur();
1132
};
1133
1134
document.getElementById("btn_rght").onclick = function() {
1135
	tabs[tab].cur++;
1136
	if (tabs[tab].cur === tabs[tab].max) this.disabled = true;
1137
	document.getElementById("btn_left").disabled = false;
1138
	updateTab();
1139
	this.blur();
1140
};
1141
1142
const buttons = document.querySelector("#main1 > .top").getElementsByTagName("button");
1143
for (let i = 0; i < buttons.length; i++) {
1144
	buttons[i].onclick = function() {
1145
		tab = i;
1146
1147
		for (let j = 0; j < buttons.length; j++) {
1148
			document.querySelector("#main1 > .mid").children[j].hidden = (tab !== j);
1149
			buttons[j].disabled = (tab === j);
1150
		}
1151
1152
		document.getElementById("btn_left").disabled = (tabs[tab].cur === 0);
0 ignored issues
show
Bug introduced by
The variable tab is changed as part of the for loop for example by i on line 1145. Only the value of the last iteration will be visible in this function if it is called after the loop.
Loading history...
1153
		document.getElementById("btn_rght").disabled = (tabs[tab].cur === tabs[tab].max);
1154
		document.getElementById("btn_dele").disabled = !tabs[tab].btnDele;
1155
		document.getElementById("btn_updt").disabled = !tabs[tab].btnUpdt;
1156
1157
		updateTab();
1158
	};
1159
}
1160
1161
function addressCreate(addr) {
1162
	document.getElementById("btn_address_create_normal").disabled = true;
1163
	document.getElementById("btn_address_create_shield").disabled = true;
1164
1165
	ae.Address_Create(addr, function(success1) {
1166
		if (success1) {
1167
			ae.Private_Update(function(success2) {
1168
				updateAddressCounts();
1169
1170
				if (success2) {
1171
					addAddress(ae.GetAddressCount() - 1);
1172
					if (addr !== "SHIELD") document.getElementById("txt_address_create_normal").value = "";
1173
				} else {
1174
					console.log("Failed to update the Private field");
1175
				}
1176
			});
1177
		} else {
1178
			console.log("Failed to add address");
1179
			updateAddressCounts();
1180
		}
1181
	});
1182
}
1183
1184
document.getElementById("btn_address_create_normal").onclick = function() {
1185
	if (ae.GetAddressCountNormal() >= ae.GetLimitNormalA(ae.GetUserLevel()) || ae.GetAddressCountNormal() + ae.GetAddressCountShield() >= 31) return;
1186
1187
	const txtNewAddr = document.getElementById("txt_address_create_normal");
1188
	if (!txtNewAddr.reportValidity()) return;
1189
1190
	addressCreate(txtNewAddr.value);
1191
};
1192
1193
document.getElementById("btn_address_create_shield").onclick = function() {
1194
	if (ae.GetAddressCountShield() >= ae.GetLimitShieldA(ae.GetUserLevel()) || ae.GetAddressCountNormal() + ae.GetAddressCountShield() >= 31) return;
1195
1196
	addressCreate("SHIELD");
1197
};
1198
1199
document.getElementById("btn_address_update").onclick = function() {
1200
	const btn = this;
1201
	btn.disabled = true;
1202
1203
	const rows = document.getElementById("tbl_addrs").rows;
1204
1205
	for (let i = 0; i < rows.length; i++) {
1206
		ae.SetAddressAccExt(i, rows[i].getElementsByTagName("input")[0].checked);
1207
		ae.SetAddressAccInt(i, rows[i].getElementsByTagName("input")[1].checked);
1208
	}
1209
1210
	ae.Address_Update(function(success) {
1211
		if (!success) console.log("Address/Update failed");
1212
		btn.disabled = false;
1213
	});
1214
};
1215
1216
document.getElementById("btn_reg").onclick = function() {
1217
	const btn = document.getElementById("btn_reg");
1218
	const txt = document.getElementById("txt_reg");
1219
	if (!txt.reportValidity()) return;
1220
	btn.disabled = true;
1221
1222
	ae.Account_Create(txt.value, function(success) {
1223
		if (success) {
1224
			addAccountToTable(ae.Admin_GetUserCount() - 1);
1225
			txt.value = "";
1226
		}
1227
1228
		btn.disabled = false;
1229
	});
1230
};
1231
1232
document.getElementById("chk_downme").onclick = function() {document.getElementById("btn_downme").disabled = !this.checked;};
1233
document.getElementById("chk_killme").onclick = function() {document.getElementById("btn_killme").disabled = !this.checked;};
1234
1235
document.getElementById("btn_notepad_saveupl").onclick = function() {
1236
	const np = document.getElementById("txt_notepad");
1237
	np.disabled = true;
1238
1239
	let fname = prompt("Save as...", "Untitled");
0 ignored issues
show
Debugging Code Best Practice introduced by
The prompt UI element is often considered obtrusive and is generally only used as a temporary measure. Consider replacing it with another UI element.
Loading history...
1240
	if (!fname.endsWith(".txt")) fname += ".txt";
1241
1242
	ae.Message_Upload(fname, np.value, function(success) {
1243
		if (success) {
1244
			np.value = "";
1245
			addUploads();
1246
			document.getElementById("tbd_accs").children[0].children[1].textContent = Math.round(ae.GetTotalMsgBytes() / 1024 / 1024);
1247
		}
1248
1249
		console.log("Failed to add text");
1250
		np.disabled = false;
1251
	});
1252
};
1253
1254
document.getElementById("btn_upload").onclick = function() {
1255
	const btn = this;
1256
	const fileSelector = document.createElement("input");
1257
	fileSelector.type = "file";
1258
	fileSelector.click();
1259
1260
	fileSelector.onchange = function() {
1261
		btn.disabled = true;
1262
1263
		const reader = new FileReader();
1 ignored issue
show
Bug introduced by
The variable FileReader seems to be never declared. If this is a global, consider adding a /** global: FileReader */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
1264
		reader.onload = function() {
1265
			ae.Message_Upload(fileSelector.files[0].name, new Uint8Array(reader.result), function(success) {
1266
				if (success) {
1267
					addUploads();
1268
					document.getElementById("tbd_accs").children[0].children[1].textContent = Math.round(ae.GetTotalMsgBytes() / 1024 / 1024);
1269
				} else {
1270
					console.log("Failed upload");
1271
				}
1272
1273
				btn.disabled = false;
1274
			});
1275
		};
1276
1277
		reader.readAsArrayBuffer(fileSelector.files[0]);
1278
	};
1279
};
1280
1281
document.getElementById("btn_pg").onclick = function() {
1282
	localStorage.greeting = document.getElementById("txt_pg").value;
1 ignored issue
show
Bug introduced by
The variable localStorage seems to be never declared. If this is a global, consider adding a /** global: localStorage */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
1283
};
1284
1285
document.querySelector("#write2_send > button").onclick = function() {
1286
	const btn = this;
1287
	btn.disabled = true;
1288
	document.getElementById("write2_btntxt").textContent = "Sending to";
1289
1290
	ae.Message_Create(
1291
		document.getElementById("write_subj").value,
1292
		document.getElementById("write_body").value,
1293
		document.getElementById("write_from").value,
1294
		document.getElementById("write_recv").value,
1295
		document.getElementById("write_subj").getAttribute("data-replyid"),
1296
		(document.getElementById("write2_recv").textContent.indexOf("@") > 0) ? null : sodium.from_base64(document.querySelector("#write2_pkey > input").value, sodium.base64_variants.ORIGINAL_NO_PADDING),
1 ignored issue
show
Bug introduced by
The variable sodium seems to be never declared. If this is a global, consider adding a /** global: sodium */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
1297
		function(success) {
1298
			if (success) {
1299
				document.getElementById("write2_btntxt").textContent = "Successfully sent to";
1300
				document.getElementById("write_recv").value = "";
1301
				document.getElementById("write_subj").value = "";
1302
				document.getElementById("write_body").value = "";
1303
			} else {
1304
				document.getElementById("write2_btntxt").textContent = "Retry sending to";
1305
				btn.disabled = false;
1306
			}
1307
		}
1308
	);
1309
};
1310
1311
document.getElementById("txt_skey").onkeyup = function(event) {
1312
	if (event.key === "Enter") {
1313
		event.preventDefault();
1314
		document.getElementById("btn_enter").click();
1315
	}
1316
};
1317
1318
document.getElementById("btn_enter").onclick = function() {
1319
	const txtSkey = document.getElementById("txt_skey");
1320
	if (!txtSkey.reportValidity()) return;
1321
1322
	const btn = this;
1323
	btn.disabled = true;
1324
	document.getElementById("txt_skey").style.background = "#233";
1325
1326
	ae.SetKeys(txtSkey.value, function(successSetKeys) {
1327
		if (successSetKeys) {
1328
			ae.Message_Browse(false, true, function(successBrowse) {
1329
				if (successBrowse) {
1330
					txtSkey.value = "";
1331
					document.getElementById("div_begin").hidden = true;
1332
					document.getElementById("div_main").style.display = "grid";
1333
					reloadAccount();
1334
1335
					if (ae.IsUserAdmin()) {
1336
						ae.Account_Browse(function(successAcc) {
1337
							if (successAcc) {for (let i = 0; i < ae.Admin_GetUserCount(); i++) {addAccountToTable(i);}}
1338
							else console.log("Failed to Account_Browse");
1339
						});
1340
					}
1341
				} else {
1342
					console.log("Failed to enter");
1343
					btn.disabled = false;
1344
					document.getElementById("txt_skey").style.background = "#466";
1345
					txtSkey.focus();
1346
				}
1347
			});
1348
		} else {
1349
			console.log("Invalid format for key");
1350
			btn.disabled = false;
1351
			document.getElementById("txt_skey").style.background = "#466";
1352
			txtSkey.focus();
1353
		}
1354
	});
1355
};
1356
1357
});
1358